package com.im.service.utils;

import com.alibaba.fastjson.JSONObject;
import com.im.codec.proto.MessagePack;
import com.im.common.constant.Constants;
import com.im.common.enums.command.Command;
import com.im.common.model.ClientInfo;
import com.im.common.model.UserSession;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 发送消息到 RabbitMQ 的生产者类
 */
@Slf4j
@Service
public class MessageProducer {

    @Autowired
    private RabbitTemplate rabbitTemplate; // RabbitMQ 模板
    @Autowired
    private UserSessionUtils userSessionUtils; // 用户会话工具类

    /**
     * 发送消息到 RabbitMQ
     *
     * @param session 用户会话
     * @param msg     消息内容
     * @return 发送成功返回 true，发送失败返回 false
     */
    public boolean sendMessage(UserSession session, Object msg) {
        try {
            log.info("向MQ发送消息：" + msg); // 打印日志
            // 第一个参数表示交换机名称，第二个参数表示使用服务器的唯一标识作为路由键，第三个参数表示要发送的消息对象
            rabbitTemplate.convertAndSend(Constants.RabbitConstants.MessageService2Im, session.getBrokerId().toString(), msg); // 发送消息到 RabbitMQ
            return true; // 发送成功
        } catch (Exception e) {
            log.error("向MQ发送消息失败：" + e.getMessage()); // 打印错误日志
            return false; // 发送失败
        }
    }

    /**
     * 包装数据并调用 sendMessage 方法发送消息到 RabbitMQ
     *
     * @param toId    接收消息的用户 ID
     * @param command 消息命令
     * @param msg     消息内容
     * @param session 用户会话
     * @return 发送成功返回 true，发送失败返回 false
     */
    public boolean sendPack(String toId, Command command, Object msg, UserSession session) {
        MessagePack messagePack = new MessagePack(); // 创建消息包对象
        messagePack.setCommand(command.getCommand()); // 设置消息命令
        messagePack.setDesc(command.getDesc()); // 设置命令名称
        messagePack.setToId(toId); // 设置接收消息的用户 ID
        messagePack.setClientType(session.getClientType()); // 设置客户端类型
        messagePack.setAppId(session.getAppId()); // 设置应用 ID
        messagePack.setImei(session.getImei()); // 设置设备 IMEI
        JSONObject jsonObject = JSONObject.parseObject(JSONObject.toJSONString(msg)); // 将消息内容转换为 JSON 对象
        messagePack.setData(jsonObject); // 设置消息内容
        String body = JSONObject.toJSONString(messagePack); // 将消息包转换为 JSON 字符串
        return sendMessage(session, body); // 调用 sendMessage 方法发送消息到 RabbitMQ
    }

    /**
     * 发送消息给指定用户的所有客户端
     *
     * @param toId    接收消息的用户 ID
     * @param command 消息命令
     * @param data    消息内容
     * @param appId   应用 ID
     * @return 发送成功的客户端信息列表
     */
    public List<ClientInfo> sendToUser(String toId, Command command, Object data, Integer appId) {
        List<UserSession> userSession = userSessionUtils.getUserSession(appId, toId); // 获取指定用户的所有会话信息
        List<ClientInfo> list = new ArrayList<>(); // 创建发送成功的客户端信息列表
        for (UserSession session : userSession) { // 遍历所有会话信息
            boolean b = sendPack(toId, command, data, session); // 调用 sendPack 方法发送消息
            if (b) { // 如果发送成功
                list.add(new ClientInfo(session.getAppId(), session.getClientType(), session.getImei())); // 将客户端信息添加到发送成功的客户端信息列表中
            }
        }
        return list; // 返回发送成功的客户端信息列表
    }

    /**
     * 发送消息给指定用户的指定客户端
     *
     * @param toId       接收消息的用户 ID
     * @param clientType 客户端类型
     * @param imei       设备 IMEI
     * @param command    消息命令
     * @param data       消息内容
     * @param appId      应用 ID
     */
    public void sendToUser(String toId, Integer clientType, String imei, Command command, Object data, Integer appId) {
        if (clientType != null && StringUtils.isNotBlank(imei)) { // 如果客户端类型和设备 IMEI 都不为空
            ClientInfo clientInfo = new ClientInfo(appId, clientType, imei); // 创建客户端信息对象
            sendToUserExceptClient(toId, command, data, clientInfo); // 调用 sendToUserExceptClient 方法发送消息
        } else { // 如果客户端类型和设备 IMEI 有一个为空
            sendToUser(toId, command, data, appId); // 调用 sendToUser 方法发送消息
        }
    }

    /**
     * 发送消息给指定用户的指定客户端
     *
     * @param toId       接收消息的用户 ID
     * @param command    消息命令
     * @param data       消息内容
     * @param clientInfo 客户端信息
     */
    public void sendToUser(String toId, Command command, Object data, ClientInfo clientInfo) {
        UserSession userSession = userSessionUtils.getUserSession(clientInfo.getAppId(), toId, clientInfo.getClientType(), clientInfo.getImei()); // 获取指定用户的指定客户端的会话信息
        sendPack(toId, command, data, userSession); // 调用 sendPack 方法发送消息
    }

    /**
     * 判断会话信息和客户端信息是否匹配
     *
     * @param sessionDto 会话信息
     * @param clientInfo 客户端信息
     * @return 匹配返回 true，不匹配返回 false
     */
    private boolean isMatch(UserSession sessionDto, ClientInfo clientInfo) {
        return Objects.equals(sessionDto.getAppId(), clientInfo.getAppId()) && Objects.equals(sessionDto.getImei(), clientInfo.getImei()) && Objects.equals(sessionDto.getClientType(), clientInfo.getClientType()); // 判断会话信息和客户端信息是否匹配
    }

    /**
     * 发送消息给指定用户的除某个客户端外的其他客户端
     *
     * @param toId       接收消息的用户 ID
     * @param command    消息命令
     * @param data       消息内容
     * @param clientInfo 客户端信息
     */
    public void sendToUserExceptClient(String toId, Command command, Object data, ClientInfo clientInfo) {
        List<UserSession> userSession = userSessionUtils.getUserSession(clientInfo.getAppId(), toId); // 获取指定用户的所有会话信息
        for (UserSession session : userSession) { // 遍历所有会话信息
            if (!isMatch(session, clientInfo)) { // 如果会话信息和客户端信息不匹配
                sendPack(toId, command, data, session); // 调用 sendPack 方法发送消息
            }
        }
    }

}